# 
# This code tests the timer, switches and LEDS
# 
# When the switches interrupt they are stored to the LEDS.
# When the timer interrupts (every 300ms) the LEDS are right shifted.
#  
# This program utilises NON-STANDARD peripherals so it is definirely a good idea to document that.
# Unpredictable results will ensue if this program is run on a different configuration.
#
# MMIO offsets from MMIO offset = 0xffff0000
# OFFSET	| CONTROLLER
#		|
# 0x0		| leds
# 0x4		| switches
# 0x8		| 
# 0xc		| timer
# 0x10		| 
# 0x14		| N/A
#		| 
# etc.		| etc.
# 
# NOTE: on bits and bit ranges in comments. ranges of bits are referenced from most significant bit to least: [MSB:LSB] throughout. verilog style.
# if bit range is only one it WILL NOT be written as [i] so as to differentiate from array indexing.
#
# NOTE: additional comments '###' or '#####' are just notes that you might take interest in and are not necessarily things that you would usually comment.

la $t0, isr		# load address of label isr.
			### a very convenient pseudo-instruction. We could jump to this label. or in this case let the cpu jump
			###to it with the next instruction in the event of an interrupt or exception.
mtc0 $t0, $15		# set ebase = isr	# interrupt service routine for ALL interrupts is now the code at label 'isr'.

start:	
	li $t0, 0xFFFF00C0	# set offset for mmio timer
	
	ori $t1 , $0 , 0x1	## 1 << 0 = 1
	sw $t1, 0x30($t0)	# timer.repeats[0] = true. (repeats_offset = 12*4 = 0xC<<2 = 0x30 )
	
	# enable timer interrupts...
	####ori $t1 , 0x1
	#li $t1 , 0xffffffff
	
	ori $t1 , $0 , 0x1	## 1 << 0 = 1
	sw $t1 , 0x34($t0)  # timer.interruptEnables[0] = true. enableInterrupts offset = 0x34
	
	#### initiate the timer!
	lui $t1 , 0x20		# $t1 = 0x2000000
	sw $t1, 0x0($t0)	# timer.interval[0] = 16e6 ticks = 300ms @ 50MHz
	
loop: j loop           # loop forever
	nop
	
isr:	mfc0 $k0, $13	# load the cause register - this is how we find out what caused the interrupt. $13 is the cause register.
			### The cause of hardware interrupts is from [25:10] (16 mmio controllers!), The cause of software interrupts is from [9:8]
			##### $k0 and $k1 are reserved kernel registers. This is essentially what an Operating System will use
			##### to respond to interrupts. The processer can't just start using other registers as when it returns (eret)
			##### the program expects its registers to remain intact.
				
	srl  $k0, $k0, 10	# $k0[15:0] = $k0[25:10] - 'right aligning' for ease of use. the hardware that caused the interrupt.
	
	andi $k0 , $k0 , 0x2	# bitmasking. k0 = k0 & 0000000000000010 = id 2 = id of switches
				# here we are disregarding everything except the second bit (0x2) of $k0 (the shifted cause register)
				### This will be 1 if the switches caused the interrupt or 0 if something else caused it.
				### By masking this we are ignoring the other bits (setting them to zero)
				
	beq  $k0 , $0 , not_sw	# if this register is not caused by the switches ( $k0 == 0 ) jump to code at label not_sw
	nop
	
	li $k1 , 0xffff0040	# set offset for switches
	
	lw $k0 , ($k1)		# $k0 = switches
				##### reading from switches will also turn off the interrupt if the switches peripheral is functioning correctly!

	li $k1 , 0xffff0000	# set offset for leds
	sw $k0 , ($k1)		# leds = switches ( = $k0 )
	
	eret
	nop
	
not_sw:	
	#naively assuming it's the switches
	
	lui $k1 , 0xffff
	lw $k0 , 0x0($k1)
	srl $k0 , $k0 , 1
	sw $k0 , 0x0($k1)	# leds = leds >> 1
	
	li $k1 , 0xffff00f8	# address of interrupts of timer.
	lw $31 , ($k1)		# acknowledge interrupt(s) from timer. and IGNORE them. haha.
	
	eret                   # otherwise eret, we aren't handling anything else
	
	nop

.data 0xffff0000
mmio: .space 0xfffc
